package POE::Component::Gearman::Client;

use 5.006;
use strict;
use warnings FATAL => 'all';

use Smart::Comments;

use POE qw(
    Component::Gearman::Connection
    Component::Gearman::Job
);

=head1 NAME

POE::Component::Gearman::Client - Asynchronous Gearman client for POE applications 

=head1 VERSION

Version 0.01

=cut

our $VERSION = '0.01';

=head1 SYNOPSIS

    use POE qw(Component::Gearman);

    spawn POE::Component::Gearman {
        Alias       => 'Gearman',
        JobServers  => [ '127.0.0.1', '192.168.0.1:123' ],
    };

    run POE::Kernel;

=head1 EXPORT

A list of functions that can be exported.  You can delete this section
if you don't export anything, such as for a purely object-oriented module.

=head1 SUBROUTINES/METHODS

=head2 spawn

=cut

sub spawn {
    shift if defined $_[0] and $_[0] eq __PACKAGE__;
    my $cfg = shift || {};

    create POE::Session(
        package_states => [
            (__PACKAGE__) => [qw(
                _start on_connection_ready
                do
                on_JOB_CREATED
                on_WORK_DATA on_WORK_WARNING
                on_WORK_STATUS on_WORK_COMPLETE
                on_WORK_FAIL on_WORK_EXCEPTION
            )],
            'POE::Component::Gearman::Connection' => [qw(
                on_tcp_connected on_connect_error reconnect
                on_read_write_error
                on_recv_command send_command
            )],
        ],
        heap => {
            box => {},
            cfg => $cfg,
        },
    );

    return;
}

sub _start {
    my ($kernel, $heap) = @_[ KERNEL, HEAP ];
    my $cfg = $heap->{cfg};

    $cfg->{Alias}       ||= 'GearmanClient';
    $cfg->{JobServer}   ||= '127.0.0.1';
    $cfg->{Prefix}      ||= '';

    $kernel->alias_set( $cfg->{Alias} );

    my $connStr = $cfg->{JobServer};
    1 while $connStr =~ s/^(\s+)|(\s+)$//g;

    my ($host, $port);
    if ( $connStr =~ /^(.*):(\d+)$/ ){
        $host = $1;
        $port = $2;
    }
    else{
        $host = $connStr;
        $port = 4730;
    }

    my $wheel = new POE::Component::Gearman::Connection( $host, $port );
    my $id = $wheel->ID;

    $heap->{connections}{$id} = {
        connStr     => $connStr,
        state       => 'connect',
        host        => $host,
        port        => $port,
        sock_wheel  => $wheel,
        pending_req => [],
        req_queue   => [],
        jobMap      => {},
    };
}

=head2 


=cut

sub on_connection_ready {
    my ($kernel, $heap) = @_[ KERNEL, HEAP ];
    my ($connID) = @_[ ARG0..$#_ ];

    my $connDesc = $heap->{connections}{$connID};
    $connDesc->{state} = 'ready';

    while( my $task = shift @{ $connDesc->{pending_req} } ){
        push @{ $connDesc->{req_queue} }, $task;
        $kernel->yield( send_command => $connID => SUBMIT_JOB => $task->{func} => '' => $task->{data} );
    }
}

=head2 

    $kernel->post( GermanClient => do => reverse => $data => {
        on_complete     => 
    });

=cut

sub do {
    my ($kernel, $heap) = @_[ KERNEL, HEAP ];
    my $sender = $_[ SENDER ];
    my ($func, $data, $options) = @_[ ARG0..$#_ ];

    my ($connID) = keys %{ $heap->{connections} };

    foreach my $cb ( qw(on_data on_warning on_status on_complete on_fail on_exception) ){
        if ( exists $options->{$cb} ){
            $options->{$cb} = {
                fn      => shift @{ $options->{$cb} },
                args    => $options->{$cb},
            };
        }
    }

    my $task = {
        func    => $func,
        sender  => $sender,
        options => $options,
        data    => $data,
    };

    my $connDesc = $heap->{connections}{$connID};
    if ( $connDesc->{state} ne 'ready' ){
        push @{ $connDesc->{pending_req} }, $task;
    ### foo: $_[SESSION]->ID
    ### $connDesc
        return;
    }

    push @{ $connDesc->{req_queue} }, $task;
    $kernel->yield( send_command => $connID => SUBMIT_JOB => $func => '' => $data );
}

sub on_JOB_CREATED {
    my ($kernel, $heap) = @_[ KERNEL, HEAP ];
    my ($jobHandle) = @{ $_[ARG0] };
    my $connID = $_[ ARG1 ];

    my $task = shift @{ $heap->{connections}{$connID}->{req_queue} };
    $heap->{connections}{$connID}{jobMap}->{$jobHandle} = $task;
    $task->{jobHandle} = $jobHandle;
}

sub on_WORK_DATA {
    my ($kernel, $heap) = @_[ KERNEL, HEAP ];
    my ($jobHandle, $data) = @{ $_[ARG0] };
    my $connID = $_[ ARG1 ];

    $data = '' if not defined $data;

    my $task = $heap->{connections}{$connID}{jobMap}->{$jobHandle};
    my $cb = $task->{options}->{on_data} or return;

    $kernel->post( $task->{sender}, $cb->{fn}, $cb->{args}, $data );
}

sub on_WORK_WARNING {
    my ($kernel, $heap) = @_[ KERNEL, HEAP ];
    my ($jobHandle, $data) = @{ $_[ARG0] };
    my $connID = $_[ ARG1 ];

    $data = '' if not defined $data;

    my $task = $heap->{connections}{$connID}{jobMap}->{$jobHandle};
    my $cb = $task->{options}->{on_warning} or return;

    $kernel->post( $task->{sender}, $cb->{fn}, $cb->{args}, $data );
}

sub on_WORK_STATUS {
    my ($kernel, $heap) = @_[ KERNEL, HEAP ];
    my ($jobHandle, $numerator, $denominator) = @{ $_[ARG0] };
    my $connID = $_[ ARG1 ];

    my $task = $heap->{connections}{$connID}{jobMap}->{$jobHandle};
    my $cb = $task->{options}->{on_status} or return;

    $kernel->post( $task->{sender}, $cb->{fn}, $cb->{args}, $numerator, $denominator );
}

sub on_WORK_COMPLETE {
    my ($kernel, $heap) = @_[ KERNEL, HEAP ];
    my ($jobHandle, $data) = @{ $_[ARG0] };
    my $connID = $_[ ARG1 ];

    $data = '' if not defined $data;

    my $task = $heap->{connections}{$connID}{jobMap}->{$jobHandle};
    my $cb = $task->{options}->{on_complete} or return;

    $kernel->post( $task->{sender}, $cb->{fn}, $cb->{args}, $data );
}

sub on_WORK_FAIL {
    my ($kernel, $heap) = @_[ KERNEL, HEAP ];
    my ($jobHandle) = @{ $_[ARG0] };
    my $connID = $_[ ARG1 ];

    my $task = $heap->{connections}{$connID}{jobMap}->{$jobHandle};
    my $cb = $task->{options}->{on_fail} or return;

    $kernel->post( $task->{sender}, $cb->{fn}, $cb->{args} );
}

sub on_WORK_EXCEPTION {
    my ($kernel, $heap) = @_[ KERNEL, HEAP ];
    my ($jobHandle, $data) = @{ $_[ARG0] };
    my $connID = $_[ ARG1 ];

    $data = '' if not defined $data;

    my $task = $heap->{connections}{$connID}{jobMap}->{$jobHandle};
    my $cb = $task->{options}->{on_exception} or return;

    $kernel->post( $task->{sender}, $cb->{fn}, $cb->{args}, $data );
}

1;

__END__
todo list:
